unit LCDMain;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, Forms,
  StdCtrls, Buttons, JvHidControllerClass, Dialogs;

const
  cCodeMercenariesVID = $07C0;
  cIOWarriorPID       = $1500;
  cLCDEnableReportID  = $04;
  cLCDWriteReportID   = $05;
  cLCDReadReportID    = $06;
  cEnable             = $01;
  cDisable            = $00;

type
  TIOWarriorOutputReport = packed record
    ReportID: Byte;
    LCDBytes: array [1..7] of Byte;
  end;

  TLCDMainForm = class(TForm)
    HidCtl: TJvHidDeviceController;
    IOWarriorAvailable: TLabel;
    HistoryListBox: TListBox;
    EnableChk: TCheckBox;
    WriteBtn: TSpeedButton;
    Write1: TEdit;
    Write2: TEdit;
    Write3: TEdit;
    Write4: TEdit;
    Write5: TEdit;
    Write6: TEdit;
    Write7: TEdit;
    ReadBtn: TSpeedButton;
    Read1: TEdit;
    SaveBtn: TSpeedButton;
    SaveDialog: TSaveDialog;
    RSBit: TCheckBox;
    procedure FormActivate(Sender: TObject);
    procedure HidCtlDeviceChange(Sender: TObject);
    procedure EnableChkClick(Sender: TObject);
    procedure WriteBtnClick(Sender: TObject);
    procedure ReadBtnClick(Sender: TObject);
    procedure SaveBtnClick(Sender: TObject);
    procedure HidCtlDeviceData(HidDev: TJvHidDevice; ReportID: Byte;
      const Data: Pointer; Size: Word);
  public
    Edits: array [1..7] of TEdit;
    IOWarrior: TJvHidDevice;
  end;

var
  LCDMainForm: TLCDMainForm;

implementation

{$R *.DFM}

procedure TLCDMainForm.FormActivate(Sender: TObject);
var
  I: Integer;
begin
  // place the edit controls in an array for easy access
  if not Assigned(Edits[Low(Edits)]) then
    for I := Low(Edits) to High(Edits) do
      Edits[I] := TEdit(FindComponent(Format('Write%d', [I])));
end;

function FindLcdIOWarrior(HidDev: TJvHidDevice): Boolean; stdcall;
begin
  // the IO-Warrior shows up as two devices
  // we want access to the IO-Warrior device for the LCD module
  // the other one with a InputReportByteLength of 5 is for access to
  // the IO pins
  // It is quite interesting to use the IO-WarriorDemo in parallel!
  Result := (HidDev.Attributes.VendorID = cCodeMercenariesVID) and
     (HidDev.Attributes.ProductID = cIOWarriorPID) and
     (HidDev.Caps.InputReportByteLength = 8);
end;

procedure TLCDMainForm.HidCtlDeviceChange(Sender: TObject);
begin
  // Free the device object if it has been unplugged
  if Assigned(IOWarrior) and not IOWarrior.IsPluggedIn then
  begin
    FreeAndNil(IOWarrior);
    // throw away the log (better save it before unplugging)
    HistoryListBox.Items.Clear;
    EnableChk.Checked := False;
    EnableChk.Enabled := False;
  end;

  // if no IO-Warrior in use yet then search for one
  if not Assigned(IOWarrior) then
    if HidCtl.CheckOutByCallback(IOWarrior, FindLcdIOWarrior) then
    begin
      EnableChk.Enabled := True;
      EnableChk.Checked := True;
    end;
  // an alternative to "if Assigned(IOWarrior) then"
  if HidCtl.CountByID(cCodeMercenariesVID, cIOWarriorPID) > 0 then
    IOWarriorAvailable.Caption := 'IO-Warrior found'
  else
    IOWarriorAvailable.Caption := 'No IO-Warrior found';
end;

procedure TLCDMainForm.HidCtlDeviceData(HidDev: TJvHidDevice; ReportID: Byte;
  const Data: Pointer; Size: Word);
var
  I: Integer;
  Str: string;
  P: PChar;
begin
  P := Data;
  // show all bytes of the report in hex
  Str := Format('R %.2x  ', [ReportID]);
  for I := 0 to Size-1 do
    Str := Str + Format('%.2x ', [Byte(P[I])]);
  Str := Str + '  "';
  // only show the net content as letters
  for I := 1 to (Integer(P[0]) and $07) - 1 do
    if Byte(P[I]) >= Byte(' ') then
      Str := Str + P[I]
    else
      Str := Str + '.';
  Str := Str + '"';
  HistoryListBox.ItemIndex := HistoryListBox.Items.Add(Str);
end;

procedure TLCDMainForm.EnableChkClick(Sender: TObject);
var
  I: Integer;
  BytesWritten: Cardinal;
  IOWarriorOutputReport: TIOWarriorOutputReport;
begin
  if Assigned(IOWarrior) then
    with IOWarriorOutputReport do
    begin
      // initialize the output report to enable/disable the LCD module
      ReportID := cLCDEnableReportID;
      for I := Low(LCDBytes) to High(LCDBytes) do
        LCDBytes[I] := $00;
      if EnableChk.Checked then
        LCDBytes[Low(LCDBytes)] := cEnable
      else
        LCDBytes[Low(LCDBytes)] := cDisable;
      // write the bits to the IO-Warrior to enable/disable the LCD
      IOWarrior.WriteFile(IOWarriorOutputReport, SizeOf(IOWarriorOutputReport), BytesWritten);
      WriteBtn.Enabled := EnableChk.Checked;
      ReadBtn.Enabled  := EnableChk.Checked;
    end;
end;

procedure TLCDMainForm.WriteBtnClick(Sender: TObject);
var
  I: Integer;
  Written: Cardinal;
  Str: string;
  IOWarriorOutputReport: TIOWarriorOutputReport;
begin
  with IOWarriorOutputReport do
  begin
    ReportID := cLCDWriteReportID;
    // get the input from the user
    for I := Low(Edits) to High(Edits) do
    begin
      LCDBytes[I] := StrToIntDef('$' + Edits[I].Text, 0);
      Edits[I].Text := Format('%.2x', [LCDBytes[I]]);
    end;
    // reset unused bits in first byte
    LCDBytes[1] := LCDBytes[1] and $07;
    // add the RS bit from the checkbox
    if RSBit.Checked then
      LCDBytes[1] := LCDBytes[1] or $80;
    IOWarrior.WriteFile(IOWarriorOutputReport, 8, Written);

    // show the data in the history listbox
    Str := Format('W %.2x  ', [ReportID]);
    for I := 1 to Written-1 do
      Str := Str + Format('%.2x ', [LCDBytes[I]]);
    HistoryListBox.ItemIndex := HistoryListBox.Items.Add(Str);
  end;
end;

procedure TLCDMainForm.ReadBtnClick(Sender: TObject);
var
  I: Integer;
  Written: Cardinal;
  Str: string;
  IOWarriorOutputReport: TIOWarriorOutputReport;
begin
  // tell the device to send back the requested amount of bytes
  // the read thread of OnData event will pick up the answer
  // it would be easier to read after write, but a thread is fail safe
  with IOWarriorOutputReport do
  begin
    // clear the report
    ReportID := cLCDReadReportID;
    for I := Low(LCDBytes) to High(LCDBytes) do
      LCDBytes[I] := $00;
    // get number of bytes to read, ensure the max of 63 and reset unused bits
    LCDBytes[1] := StrToIntDef('$' + Read1.Text, 0) and $3F;
    // add the RS bit from the checkbox
    if RSBit.Checked then
      LCDBytes[1] := LCDBytes[1] or $80;
    IOWarrior.WriteFile(IOWarriorOutputReport, 8, Written);

    // show the data in the history listbox
    Str := Format('W %.2x  ', [ReportID]);
    for I := 1 to Written-1 do
      Str := Str + Format('%.2x ', [LCDBytes[I]]);
    HistoryListBox.ItemIndex := HistoryListBox.Items.Add(Str);
  end;
end;

procedure TLCDMainForm.SaveBtnClick(Sender: TObject);
begin
  if SaveDialog.Execute then
    HistoryListBox.Items.SaveToFile(SaveDialog.FileName);
end;

end.
